home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 April / EnigmA AMIGA RUN 17 (1997)(G.R. Edizioni)(IT)[!][issue 1997-04][EAR-CD].iso / EARCD / text / edit / envwrd41.lha / envWRD41 / source / words / funcs.c < prev    next >
C/C++ Source or Header  |  1996-11-11  |  51KB  |  2,003 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  ApiLib ©1996 Dietmar Eilert
  4.  
  5.  ISpell interface for GoldED 4, ©1996 Dietmar Eilert.
  6.  
  7.  Dice:
  8.  
  9.  DMAKE
  10.  
  11.  -------------------------------------------------------------------------------
  12.  
  13. */
  14.  
  15. #include "defs.h"
  16.  
  17. /// "prototypes"
  18.  
  19. // library functions
  20.  
  21. Prototype LibCall struct APIClient *APIMountClient(__A0 struct APIMessage *, __A1 char *);
  22. Prototype LibCall void              APICloseClient(__A0 struct APIClient  *, __A1 struct APIMessage *);
  23. Prototype LibCall void              APIBriefClient(__A0 struct APIClient  *, __A1 struct APIMessage *);
  24. Prototype LibCall void              APIFree       (__A0 struct APIClient  *, __A1 struct APIOrder   *);
  25.  
  26. // private functions
  27.  
  28. Prototype struct FileInfoBlock     *FileInfo(UBYTE *, struct FileInfoBlock *);
  29. Prototype void                      Beep(UWORD);
  30. Prototype struct Node              *SearchNode(struct List *, UWORD);
  31. Prototype UWORD                     ComputeX(struct TextFont *, UWORD);
  32. Prototype UWORD                     ComputeY(struct TextFont *, UWORD);
  33. Prototype void                      GetVScreenSize(struct Screen *, UWORD *, UWORD *);
  34. Prototype struct MsgPort           *RunISpell(void);
  35. Prototype struct Window            *OpenMessageWin(UBYTE *, BOOL);
  36. Prototype struct Window            *Message       (UBYTE *, BOOL, struct Window *);
  37. Prototype BOOL                      ExistPath(UBYTE *);
  38. Prototype UBYTE                    *SendRexxCommand(UBYTE *, UBYTE *, struct MsgPort *, UBYTE *);
  39. Prototype LONG                      CommandSpell     (struct APIClient *, struct APIMessage *, ULONG *);
  40. Prototype void                      IgnoreWordCurrent(struct APIClient *, struct APIMessage *, BOOL);
  41. Prototype BOOL                      CheckWord        (struct APIClient *, struct APIMessage *, UBYTE *, UWORD, UBYTE *);
  42. Prototype void                      CheckWordCurrent (struct APIClient *, struct APIMessage *, BOOL);
  43. Prototype void                      LearnWord        (struct APIClient *, struct APIMessage *, UBYTE *, UWORD, BOOL);
  44. Prototype void                      LearnWordCurrent (struct APIClient *, struct APIMessage *, BOOL);
  45. Prototype void                      Dispatch         (struct APIClient *, struct APIMessage *);
  46. Prototype void                      ShowSpell        (struct APIClient *, struct APIMessage *, struct List *, UBYTE *, UBYTE *);
  47. Prototype void                      Replace          (struct APIClient *, struct APIMessage *, UWORD, UWORD, UBYTE *);
  48. Prototype BOOL                      DoExecute        (UBYTE *, BOOL, UBYTE *, UBYTE *, ULONG, WORD);
  49. Prototype struct MsgPort           *WaitForISpell(void);
  50. Prototype void                      StopISpell(void);
  51. Prototype void                      Uncapitalize(UBYTE *);
  52.  
  53. // private defines
  54.  
  55. #define UNDEFINED  ((UWORD)~0)
  56.  
  57. // buffer handles are allocated to store text-local data
  58.  
  59. struct BufferHandle {
  60.  
  61.     struct APIClient        APIClient;               // API handle
  62.     struct APIOrder         APIOrder;                // API order
  63.     struct APIModifyRequest APIModifyRequest;        // API modification request
  64.     UBYTE                   APIData[4096];           // API buffer
  65.     UBYTE                   Buffer [4096];           // private ARexx buffer
  66.     struct MsgPort         *Port;                    // private ARexx message port
  67. };
  68.  
  69. // AutoConfig semaphore coordinates ISpell usage
  70.  
  71. struct AutoConfig {
  72.  
  73.     struct SignalSemaphore Semaphore;
  74.     UWORD                  Users;                    // ISpell users
  75.     ULONG                  Words;                    // new words counter
  76. };
  77.  
  78. ///
  79. /// "globals"
  80.  
  81. struct AutoConfig      *AutoConfig;
  82. struct SignalSemaphore  ISpellSemaphore;
  83. BOOL                    IsLetter  [256];             // valid letters
  84. BOOL                    IsBoundary[256];             // valid letter OR space (depends on context)
  85. BOOL                    IsTrigger [256];             // trigger characters
  86. BOOL                    Online;                      // online spelll checking ?
  87. UWORD                   Volume;                      // beep volume
  88.  
  89. ///
  90. /// "library functions"
  91.  
  92. LibCall struct APIClient *
  93. APIMountClient(__A0 struct APIMessage *apiMsg, __A1 char *args)
  94. {
  95.     struct BufferHandle *bufferHandle = NULL;
  96.  
  97.     // start ISpell (if not yet running)
  98.  
  99.     if (RunISpell()) {
  100.  
  101.         // allocate local data for this editor window (extended APIClient structure)
  102.  
  103.         if (bufferHandle = (struct BufferHandle *)AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  104.  
  105.             if (bufferHandle->Port = CreateMsgPort()) {
  106.  
  107.                 static UBYTE *apiCommands[] = { "SPELL PREV/S,NEXT/S,ADD/S,CURRENT/S,ASK/S", NULL };
  108.  
  109.                 // describe our features
  110.  
  111.                 bufferHandle->APIClient.api_APIVersion = API_INTERFACE_VERSION;
  112.                 bufferHandle->APIClient.api_Version    = 2;
  113.                 bufferHandle->APIClient.api_Name       = "Words";
  114.                 bufferHandle->APIClient.api_Info       = "ISpell interface for GoldED";
  115.                 bufferHandle->APIClient.api_Commands   = NULL;
  116.                 bufferHandle->APIClient.api_Serial     = 0;
  117.                 bufferHandle->APIClient.api_Classes    = API_CLASS_SYSTEM | API_CLASS_COMMAND;
  118.                 bufferHandle->APIClient.api_Area       = NULL;
  119.  
  120.                 if (Online)
  121.                     bufferHandle->APIClient.api_Classes |= API_CLASS_KEY;
  122.             }
  123.         }
  124.     }
  125.  
  126.     return((struct APIClient *)bufferHandle);
  127. }
  128.  
  129.  
  130. LibCall void
  131. APICloseClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg)
  132. {
  133.     struct BufferHandle *bufferHandle = (struct BufferHandle *)handle;
  134.  
  135.     // free local data (allocated for a specific editor window)
  136.  
  137.     if (bufferHandle->Port)
  138.  
  139.         DeleteMsgPort(bufferHandle->Port);
  140.  
  141.     FreeVec(bufferHandle);
  142.  
  143.     StopISpell();
  144. }
  145.  
  146.  
  147. LibCall void
  148. APIBriefClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg)
  149. {
  150.     struct APIMessage *msg;
  151.  
  152.     // handle host's command notify
  153.  
  154.     for (msg = apiMsg; msg; msg = msg->api_Next) {
  155.  
  156.         if (msg->api_State == API_STATE_NOTIFY) {
  157.  
  158.             // process message sent by GoldED
  159.  
  160.             switch (msg->api_Class) {
  161.  
  162.                 // did user press a key ?
  163.  
  164.                 case API_CLASS_KEY:
  165.  
  166.                     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  167.  
  168.                     switch (msg->api_Action) {
  169.  
  170.                         case API_ACTION_VANILLAKEY:
  171.  
  172.                             // trigger key used ?
  173.  
  174.                             if (Online) {
  175.  
  176.                                 if (IsTrigger[(UWORD)apiMsg->api_Data]) {
  177.  
  178.                                     // spellcheck last word
  179.  
  180.                                     CheckWordCurrent(handle, apiMsg, FALSE);
  181.                                 }
  182.                             }
  183.  
  184.                             break;
  185.                     }
  186.  
  187.                     break;
  188.  
  189.                 // did user enter a command ?
  190.  
  191.                 case API_CLASS_COMMAND:
  192.  
  193.                     switch (msg->api_Action) {
  194.  
  195.                         case API_ACTION_COMMAND:
  196.  
  197.                             Dispatch(handle, msg);
  198.  
  199.                             break;
  200.  
  201.                         default:
  202.  
  203.                             msg->api_Error = API_ERROR_UNKNOWN;
  204.                     }
  205.  
  206.                     break;
  207.  
  208.                 default:
  209.  
  210.                     msg->api_Error = API_ERROR_UNKNOWN;
  211.             }
  212.         }
  213.     }
  214. }
  215.  
  216.  
  217. LibCall void
  218. APIFree(__A0 struct APIClient *handle, __A1 struct APIOrder *apiOrder)
  219. {
  220.     ;
  221. }
  222.  
  223. ///
  224. /// "dispatcher"
  225.  
  226. /* --------------------------------- Dispatch ----------------------------------
  227.  
  228.  Handle command sent by GoldED. This API client adds commands (currently only
  229.  the SPELL command) to GoldED's command set. This function is called if GoldED
  230.  detects an command that needs to be handled by an API client (e.g. us).
  231.  
  232. */
  233.  
  234. void
  235. Dispatch(handle, apiMsg)
  236.  
  237. struct APIClient  *handle;
  238. struct APIMessage *apiMsg;
  239. {
  240.     struct RDArgs *rdArgs;
  241.  
  242.     if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {
  243.  
  244.         // table of supported commands, handlers & template strings
  245.  
  246.         static struct parser { char *command; LONG (*handler)(struct APIClient *, struct APIMessage *, ULONG *); char *template; } parser[] = {
  247.  
  248.             "SPELL", (APTR)CommandSpell, "CURRENT/S,ADD/S,IGNORE/S,NOCASE/S,SAVE/S",
  249.              NULL
  250.         };
  251.  
  252.         ULONG argArray[10];
  253.         UBYTE buffer  [80];
  254.         ULONG n;
  255.  
  256.         memset(argArray, 0, sizeof(argArray));
  257.  
  258.         // make LF-terminated copy of command string (required by dos/readArgs):
  259.  
  260.         strncpy(buffer, apiMsg->api_Command, sizeof(buffer));
  261.  
  262.         strcat(buffer, "\12");
  263.  
  264.         for (n = 0; parser[n].command; ++n) {
  265.  
  266.             if (memcmp(buffer, parser[n].command, strlen(parser[n].command)) == 0) {
  267.  
  268.                 UBYTE *arguments = buffer + strlen(parser[n].command);
  269.  
  270.                 // tell host that message has been consumed by us (hide it from other API clients)
  271.  
  272.                 apiMsg->api_State = API_STATE_CONSUMED;
  273.  
  274.                 rdArgs->RDA_Source.CS_Buffer = arguments;
  275.                 rdArgs->RDA_Source.CS_Length = strlen(arguments);
  276.                 rdArgs->RDA_Source.CS_CurChr = 0;
  277.                 rdArgs->RDA_DAList           = 0;
  278.                 rdArgs->RDA_Buffer           = NULL;
  279.  
  280.                 // arguments expected ?
  281.  
  282.                 if (parser[n].template) {
  283.  
  284.                     if (ReadArgs(parser[n].template, argArray, rdArgs)) {
  285.  
  286.                         apiMsg->api_RC = (*parser[n].handler)(handle, apiMsg, argArray);
  287.  
  288.                         FreeArgs(rdArgs);
  289.                     }
  290.                     else {
  291.  
  292.                         static UBYTE error[80 + 1];
  293.  
  294.                         apiMsg->api_RC           = RC_WARN;
  295.                         apiMsg->api_CommandError = error;
  296.  
  297.                         Fault(IoErr(), "IoErr()", error, 80);
  298.                     }
  299.                 }
  300.                 else
  301.                     apiMsg->api_RC = (*parser[n].handler)(handle, apiMsg, argArray);
  302.             }
  303.         }
  304.  
  305.         FreeDosObject(DOS_RDARGS, rdArgs);
  306.     }
  307. }
  308.  
  309.  
  310. /* -------------------------------- CommandSpell -------------------------------
  311.  
  312.  SPELL command handler
  313.  
  314.  template: CURRENT/S,ADD/S,IGNORE/S,NOCASE/S,SAVE/S
  315.  
  316. */
  317.  
  318. LONG
  319. CommandSpell(handle, apiMsg, argArray)
  320.  
  321. struct APIClient  *handle;
  322. struct APIMessage *apiMsg;
  323. ULONG             *argArray;
  324. {
  325.     if (argArray[0])                                 // CURRENT/S
  326.  
  327.         CheckWordCurrent(handle, apiMsg, TRUE);
  328.  
  329.     if (argArray[1])                                 // ADD/S
  330.  
  331.         LearnWordCurrent(handle, apiMsg, argArray[3]);
  332.  
  333.     if (argArray[2])                                 // IGNORE/S
  334.  
  335.         IgnoreWordCurrent(handle, apiMsg, argArray[3]);
  336.  
  337.     if (argArray[4]) {                               // SAVE/S
  338.  
  339.         struct AutoConfig *autoConfig;
  340.  
  341.         Forbid();
  342.  
  343.         if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  344.  
  345.             ObtainSemaphore(&autoConfig->Semaphore);
  346.  
  347.             // save user dictionary
  348.  
  349.             SendRexxCommand("IRexxSpell", "ADD A", ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer);
  350.  
  351.             // reset global new words counter
  352.  
  353.             autoConfig->Words = 0;
  354.  
  355.             ReleaseSemaphore(&autoConfig->Semaphore);
  356.         }
  357.         else
  358.             Message("Communication error (semaphore not found)", TRUE, NULL);
  359.  
  360.         Permit();
  361.     }
  362.  
  363.     return(RC_OK);
  364. }
  365.  
  366. ///
  367. /// "misc"
  368.  
  369. /* --------------------------------- Uncapitalize ------------------------------
  370.  
  371.  Uncapitalize text if first character is uppercase and all other characters are
  372.  lowercase. Writes to <text>.
  373.  
  374. */
  375.  
  376. void
  377. Uncapitalize(text)
  378.  
  379. UBYTE *text;
  380. {
  381.     if (isupper(*text)) {
  382.  
  383.         UBYTE *next;
  384.  
  385.         for (next = text + 1; *next; ++next) {
  386.  
  387.             if (isupper(*next))
  388.  
  389.                 break;
  390.         }
  391.  
  392.         if (*next == 0)
  393.  
  394.             *text = tolower(*text);
  395.     }
  396. }
  397.  
  398.  
  399. /* --------------------------------- FileInfo ----------------------------------
  400.  
  401.  Return file information or NULL (writes to <fib>)
  402.  
  403. */
  404.  
  405. struct FileInfoBlock *
  406. FileInfo(name, fib)
  407.  
  408. struct FileInfoBlock *fib;
  409. UBYTE                *name;
  410. {
  411.     BPTR handle;
  412.  
  413.     if (handle = Lock(name, ACCESS_READ)) {
  414.  
  415.         if (Examine(handle, fib)) {
  416.  
  417.             UnLock(handle);
  418.  
  419.             return(fib);
  420.         }
  421.         else
  422.             UnLock(handle);
  423.     }
  424.  
  425.     return(NULL);
  426. }
  427.  
  428.  
  429. /* --------------------------------- DoExecute ---------------------------------
  430.  
  431.  Run DOS command
  432.  
  433. */
  434.  
  435. BOOL
  436. DoExecute(cmd, async, output, directory, stack, prio)
  437.  
  438. UBYTE *cmd, *output, *directory;
  439. ULONG  stack;
  440. WORD   prio;
  441. BOOL   async;
  442. {
  443.     BPTR newCurrentDir;
  444.     BOOL success;
  445.  
  446.     success = FALSE;
  447.  
  448.     if (newCurrentDir = Lock(directory, SHARED_LOCK)) {
  449.  
  450.         BPTR inHandle, outHandle, oldCurrentDir;
  451.  
  452.         oldCurrentDir = CurrentDir(newCurrentDir);
  453.  
  454.         if (outHandle = Open(output, MODE_NEWFILE)) {
  455.  
  456.             struct MsgPort *oldct, *newct;
  457.  
  458.             if (IsInteractive(outHandle)) {
  459.  
  460.                 newct = ((struct FileHandle *)BADDR(outHandle))->fh_Type;
  461.  
  462.                 oldct = SetConsoleTask(newct);
  463.  
  464.                 inHandle = Open("CONSOLE:", MODE_OLDFILE);
  465.  
  466.                 SetConsoleTask(oldct);
  467.             }
  468.             else {
  469.  
  470.                 newct = NULL;
  471.  
  472.                 inHandle = Open("NIL:", MODE_OLDFILE);
  473.             }
  474.  
  475.             if (inHandle) {
  476.  
  477.                 struct TagItem tagItems[] = {
  478.  
  479.                     SYS_Output,     (ULONG)NULL,
  480.                     SYS_Input,      (ULONG)NULL,
  481.                     NP_ConsoleTask, (ULONG)NULL,
  482.                     SYS_Asynch,     (ULONG)FALSE,
  483.                     NP_StackSize,   (ULONG)8196,
  484.                     NP_Priority,    (ULONG)0,
  485.                     SYS_UserShell,  (ULONG)TRUE,
  486.                     TAG_DONE
  487.                 };
  488.  
  489.                 tagItems[0].ti_Data = (ULONG)outHandle;
  490.                 tagItems[1].ti_Data = (ULONG)inHandle;
  491.                 tagItems[2].ti_Data = (ULONG)newct;
  492.                 tagItems[3].ti_Data = (ULONG)async;
  493.                 tagItems[4].ti_Data = (ULONG)stack;
  494.                 tagItems[5].ti_Data = (ULONG)prio;
  495.  
  496.                 if (SystemTagList(cmd, tagItems) != -1L)
  497.  
  498.                     success = TRUE;
  499.  
  500.                 if (!(async && success))
  501.  
  502.                     Close(inHandle);
  503.             }
  504.  
  505.             if (!(async && success))
  506.  
  507.                 Close(outHandle);
  508.         }
  509.  
  510.         CurrentDir(oldCurrentDir);
  511.  
  512.         UnLock(newCurrentDir);
  513.     }
  514.  
  515.     return(success);
  516. }
  517.  
  518.  
  519. /* --------------------------------- ExistPath -----------------------------------
  520.  
  521.  Check whether file/directory exists.
  522.  
  523. */
  524.  
  525. BOOL
  526. ExistPath(path)
  527.  
  528. UBYTE *path;
  529. {
  530.     BPTR lock;
  531.  
  532.     if (lock = Lock(path, ACCESS_READ))
  533.         UnLock(lock);
  534.  
  535.     if (lock)
  536.         return(TRUE);
  537.     else
  538.         return(FALSE);
  539. }
  540.  
  541.  
  542. /* ---------------------------------- SearchNode --------------------------------
  543.  
  544.  Return pointer to node if ordinal number (0, ...) is known.
  545.  
  546. */
  547.  
  548. struct Node *
  549. SearchNode(list, ordinal)
  550.  
  551. struct List *list;
  552. UWORD  ordinal;
  553. {
  554.     struct Node *node;
  555.  
  556.     for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ)
  557.  
  558.         if (ordinal == 0)
  559.  
  560.             return(node);
  561.  
  562.     return(NULL);
  563. }
  564.  
  565.  
  566. /* ----------------------------------- Beep ------------------------------------
  567.  
  568.  Short audible beep (volumn: 0...63)
  569.  
  570. */
  571.  
  572. void
  573. Beep(volume)
  574.  
  575. UWORD volume;
  576. {
  577.     if (volume) {
  578.  
  579.         struct IOAudio *audioIO;
  580.  
  581.         if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) {
  582.  
  583.             struct MsgPort *audioMP;
  584.  
  585.             if (audioMP = CreateMsgPort()) {
  586.  
  587.                 UBYTE whichannel[] = { 1, 2, 4, 8 };
  588.  
  589.                 audioIO->ioa_Request.io_Message.mn_ReplyPort   = audioMP;
  590.                 audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
  591.                 audioIO->ioa_Request.io_Command                = ADCMD_ALLOCATE;
  592.                 audioIO->ioa_Request.io_Flags                  = ADIOF_NOWAIT;
  593.                 audioIO->ioa_AllocKey                          = 0;
  594.                 audioIO->ioa_Data                              = whichannel;
  595.                 audioIO->ioa_Length                            = sizeof(whichannel);
  596.  
  597.                 if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) {
  598.  
  599.                     __chip const static UBYTE waveptr[2] = {127, -127};
  600.  
  601.                     audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
  602.                     audioIO->ioa_Request.io_Command              = CMD_WRITE;
  603.                     audioIO->ioa_Request.io_Flags                = ADIOF_PERVOL;
  604.  
  605.                     audioIO->ioa_Data   = waveptr;
  606.                     audioIO->ioa_Length = 2;
  607.                     audioIO->ioa_Period = 1015;
  608.                     audioIO->ioa_Volume = volume;
  609.                     audioIO->ioa_Cycles = 60;
  610.  
  611.                     BeginIO((struct IORequest *)audioIO );
  612.  
  613.                     WaitPort(audioMP);
  614.                     GetMsg  (audioMP);
  615.  
  616.                     CloseDevice((struct IORequest *)audioIO);
  617.                 }
  618.  
  619.                 DeleteMsgPort(audioMP);
  620.             }
  621.  
  622.             FreeVec(audioIO);
  623.         }
  624.     }
  625. }  
  626.  
  627.  
  628. /* ----------------------------------- Replace ---------------------------------
  629.  
  630.  Replace word of length <wordLen> at position <pos> within current line by
  631.  string <replacement> (0-terminated)
  632.  
  633. */
  634.  
  635. void
  636. Replace(handle, apiMsg, column, wordLen, replacement)
  637.  
  638. struct APIClient   *handle;
  639. struct APIMessage  *apiMsg;
  640. UBYTE              *replacement;
  641. UWORD               column;
  642. UWORD               wordLen;
  643. {
  644.     struct APIOrder         *apiOrder;
  645.     struct APIModifyRequest *apiModifyRequest;
  646.     UBYTE                   *apiBuffer;
  647.     struct EditConfig       *config;
  648.     UBYTE                   *dest;
  649.     UWORD                    rplcLen;
  650.  
  651.     config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  652.  
  653.     // allocate API command structures
  654.  
  655.     apiModifyRequest = &((struct BufferHandle *)handle)->APIModifyRequest;
  656.     apiOrder         = &((struct BufferHandle *)handle)->APIOrder;
  657.     apiBuffer        =  ((struct BufferHandle *)handle)->APIData;
  658.  
  659.     // copy current line to buffer
  660.  
  661.     movmem(config->CurrentBuffer, apiBuffer, config->CurrentLen);
  662.  
  663.     // replace old word by <replacement>
  664.  
  665.     dest = apiBuffer + column;
  666.  
  667.     rplcLen = strlen(replacement);
  668.  
  669.     if (rplcLen != wordLen)
  670.  
  671.         movmem(dest + wordLen, dest + rplcLen, config->CurrentLen - column - wordLen);
  672.  
  673.     movmem(replacement, dest, rplcLen);
  674.  
  675.     // create API command for GoldED
  676.  
  677.     apiModifyRequest->mr_Line   = config->Line;
  678.     apiModifyRequest->mr_Column = column;
  679.     apiModifyRequest->mr_Size   = config->CurrentLen + (rplcLen - wordLen);
  680.     apiModifyRequest->mr_Data   = apiBuffer;
  681.  
  682.     apiOrder->api_Data = apiModifyRequest;
  683.     apiOrder->api_Next = NULL;
  684.  
  685.     // notify GoldED
  686.  
  687.     apiMsg->api_Order = apiOrder;
  688. }
  689.  
  690. ///
  691. /// "gui"
  692.  
  693. /* ------------------------------ GetVScreenSize -------------------------------
  694.  
  695.  Calculate visible screen rectangle
  696.  
  697. */
  698.  
  699. void
  700. GetVScreenSize(screen, displayW, displayH)
  701.  
  702. struct Screen *screen;
  703. UWORD         *displayW;
  704. UWORD         *displayH;
  705. {
  706.     struct Rectangle clip;
  707.  
  708.     // read screen size
  709.  
  710.     if (QueryOverscan(GetVPModeID(&screen->ViewPort), &clip, OSCAN_TEXT)) {
  711.  
  712.         *displayW = clip.MaxX - clip.MinX + 1;
  713.         *displayH = clip.MaxY - clip.MinY + 1;
  714.     }
  715.     else {
  716.  
  717.         *displayW = screen->Width;
  718.         *displayH = screen->Height;
  719.     }
  720. }
  721.  
  722.  
  723. /* --------------------------------- ComputeX ----------------------------------
  724.  
  725.  Scale width <value> to match font
  726.  
  727. */
  728.  
  729. UWORD
  730. ComputeX(font, value)
  731.  
  732. struct TextFont *font;
  733. UWORD            value;
  734. {
  735.     return((font->tf_XSize * value) / 8);
  736. }
  737.  
  738.  
  739. /* --------------------------------- ComputeY ----------------------------------
  740.  
  741.  Resize height <value> to match font
  742.  
  743. */
  744.  
  745. UWORD
  746. ComputeY(font, value)
  747.  
  748. struct TextFont *font;
  749. UWORD            value;
  750. {
  751.     return((font->tf_YSize * value) / 8);
  752. }
  753.  
  754.  
  755. /* -------------------------------- OpenMessageWin -----------------------------
  756.  
  757.  Open info window. Window is closed automatically after a short delay time if
  758.  <sync> is TRUE.
  759.  
  760. */
  761.  
  762. struct Window *
  763. OpenMessageWin(text, sync)
  764.  
  765. UBYTE *text;
  766. BOOL   sync;
  767. {
  768.     struct TextFont *font;
  769.     struct Screen   *screen;
  770.     struct Window   *win;
  771.     ULONG            lock;
  772.  
  773.     win  = NULL;
  774.     font = NULL;
  775.  
  776.     // critical stuff (barely legal - don't do this at home, kids ;-)
  777.  
  778.     lock = LockIBase(0);
  779.  
  780.     // find frontmost screen
  781.  
  782.     screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen;
  783.  
  784.     // keep system frozen
  785.  
  786.     Forbid();
  787.  
  788.     // enable rendering functions
  789.  
  790.     UnlockIBase(lock);
  791.  
  792.     if (screen) {
  793.  
  794.         struct TextAttr textAttr = { "topaz.font", 8, 0, FPF_DESIGNED };
  795.  
  796.         WORD len, width, height, fontW, fontH, x, y, displayW, displayH, xOffset, yOffset;
  797.  
  798.         GetVScreenSize(screen, &displayW, &displayH);
  799.  
  800.         xOffset = -screen->ViewPort.DxOffset;
  801.         yOffset = -screen->ViewPort.DyOffset;
  802.  
  803.         textAttr = *screen->Font;
  804.  
  805.         if (font = OpenDiskFont(&textAttr)) {
  806.  
  807.             fontW = font->tf_XSize;
  808.             fontH = font->tf_YSize;
  809.  
  810.             CloseFont(font);
  811.         }
  812.         else
  813.             fontW = fontH = textAttr.ta_YSize;
  814.  
  815.         len = strlen(text);
  816.  
  817.         if (len < 30)
  818.             len = 30;
  819.  
  820.         width  = 20 + fontW * len;
  821.         height = 20 + fontH;
  822.  
  823.         // center window on screen
  824.  
  825.         if (displayW) {
  826.  
  827.             x = ((displayW - width )>>1) + xOffset;
  828.             y = ((displayH - height)>>1) + yOffset;
  829.         }
  830.         else
  831.             x = y = 0;
  832.  
  833.         win = OpenWindowTags(NULL,
  834.  
  835.             WA_PubScreen,     screen,
  836.             WA_Left,          x,
  837.             WA_Top,           y,
  838.             WA_InnerWidth,    width,
  839.             WA_InnerHeight,   height,
  840.             WA_Title,        "Words",
  841.             WA_ScreenTitle,  "Words",
  842.             WA_Flags,         WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE | WFLG_RMBTRAP,
  843.             WA_GimmeZeroZero, TRUE,
  844.             TAG_DONE
  845.         );
  846.  
  847.         Permit();
  848.  
  849.         // render window
  850.  
  851.         if (win) {
  852.  
  853.             if (font)
  854.                 SetFont(win->RPort, font);
  855.  
  856.             win = Message(text, sync, win);
  857.         }
  858.     }
  859.     else
  860.         Permit();
  861.  
  862.     return(win);
  863. }
  864.  
  865.  
  866. /* ---------------------------------- Message ----------------------------------
  867.  
  868.  Show message (assume that window size is sufficient)
  869.  
  870. */
  871.  
  872. struct Window *
  873. Message(text, sync, win)
  874.  
  875. struct Window *win;
  876. UBYTE         *text;
  877. BOOL           sync;
  878. {
  879.     if (win == NULL)
  880.  
  881.         win = OpenMessageWin(text, sync);
  882.  
  883.     else {
  884.  
  885.         struct RastPort *rast = win->RPort;
  886.  
  887.         SetAPen(rast, 0);
  888.         SetRast(rast, 0);
  889.         SetAPen(rast, 1);
  890.  
  891.         Move(rast, 10, 10 + rast->TxBaseline);
  892.  
  893.         Text(rast, text, strlen(text));
  894.  
  895.         if (sync) {
  896.  
  897.             Delay(150);
  898.  
  899.             CloseWindow(win);
  900.  
  901.             win = NULL;
  902.         }
  903.     }
  904.  
  905.     return(win);
  906. }
  907.  
  908.  
  909. /* --------------------------------- ShowSpell ---------------------------------
  910.  
  911.  Show ISpell suggestions. Writes user selection to suggestion.
  912.  
  913. */
  914.  
  915. void
  916. ShowSpell(handle, apiMsg, list, title, suggestion)
  917.  
  918. struct APIClient  *handle;
  919. struct APIMessage *apiMsg;
  920. struct List       *list;
  921. UBYTE             *title;
  922. UBYTE             *suggestion;
  923. {
  924.     struct Node *node;
  925.     UWORD        suggestions;
  926.  
  927.     *suggestion = 0;
  928.  
  929.     for (suggestions = 0, node = list->lh_Head; node->ln_Succ; node = node->ln_Succ)
  930.  
  931.         ++suggestions;
  932.  
  933.     if (suggestions) {
  934.  
  935.         struct Screen *screen;
  936.         struct Window *parent;
  937.  
  938.         screen = apiMsg->api_Instance->api_Screen;
  939.         parent = apiMsg->api_Instance->api_Window;
  940.  
  941.         if (screen && parent) {
  942.  
  943.             struct TextFont *font;
  944.  
  945.             if (font = OpenDiskFont(screen->Font)) {
  946.  
  947.                 APTR visualInfo;
  948.  
  949.                 if (visualInfo = GetVisualInfoA(screen, NULL)) {
  950.  
  951.                     struct Gadget *context, *glist;
  952.  
  953.                     glist = NULL;
  954.  
  955.                     if (context = CreateContext(&glist)) {
  956.  
  957.                         struct Window    *win;
  958.                         struct Gadget    *gad;
  959.                         struct NewGadget  newGad;
  960.  
  961.                         if (suggestions < 3)
  962.                             suggestions = 3;
  963.  
  964.                         newGad.ng_LeftEdge   = 5;
  965.                         newGad.ng_TopEdge    = 5;
  966.                         newGad.ng_Width      = ComputeX(font, 300);
  967.                         newGad.ng_Height     = ComputeY(font, suggestions * 8) + 4;
  968.                         newGad.ng_GadgetText = NULL;
  969.                         newGad.ng_TextAttr   = screen->Font;
  970.                         newGad.ng_GadgetID   = 0;
  971.                         newGad.ng_Flags      = 0;
  972.                         newGad.ng_VisualInfo = visualInfo;
  973.                         newGad.ng_UserData   = 0;
  974.  
  975.                         gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE);
  976.  
  977.                         if (gad) {
  978.  
  979.                             win = OpenWindowTags(NULL,
  980.  
  981.                                 WA_PubScreen, screen,
  982.                                 WA_Left,      parent->LeftEdge,
  983.                                 WA_Top,       parent->TopEdge + parent->BorderTop,
  984.                                 WA_Width,     newGad.ng_Width  + 10,
  985.                                 WA_Height,    newGad.ng_Height + 10,
  986.                                 WA_IDCMP,     LISTVIEWIDCMP | IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_CLOSEWINDOW,
  987.                                 WA_Flags,     WFLG_BORDERLESS | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
  988.                                 WA_Title,     title,
  989.                                 TAG_DONE
  990.                             );
  991.  
  992.                             if (win) {
  993.  
  994.                                 struct IntuiMessage *msg;
  995.                                 struct Node         *node;
  996.                                 UWORD                active;
  997.                                 BOOL                 isDoubleClick;
  998.                                 ULONG                lastSeconds, lastMicros, lastSelection, class;
  999.  
  1000.                                 SetRast(win->RPort, 0);
  1001.  
  1002.                                 DrawBevelBox(win->RPort, 0, 0, win->Width, win->Height, GT_VisualInfo, visualInfo, TAG_DONE);
  1003.  
  1004.                                 AddGList(win, glist, UNDEFINED, UNDEFINED, NULL);
  1005.  
  1006.                                 RefreshGList(glist, win, NULL, UNDEFINED);
  1007.  
  1008.                                 GT_RefreshWindow(win, NULL);
  1009.  
  1010.                                 active = 0;
  1011.  
  1012.                                 do {
  1013.  
  1014.                                     isDoubleClick = FALSE;
  1015.  
  1016.                                     while (!(msg = GT_GetIMsg(win->UserPort)))
  1017.  
  1018.                                         WaitPort(win->UserPort);
  1019.  
  1020.                                     switch (class = msg->Class) {
  1021.  
  1022.                                         case RAWKEY:
  1023.  
  1024.                                             WORD step = (msg->Code == CURSORUP) ? -1 : 1;
  1025.  
  1026.                                             if (node = SearchNode(list, active + step)) {
  1027.  
  1028.                                                 active += step;
  1029.  
  1030.                                                 GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE);
  1031.                                             }
  1032.  
  1033.                                             break;
  1034.  
  1035.                                         case IDCMP_REFRESHWINDOW:
  1036.  
  1037.                                             BeginRefresh(win);
  1038.  
  1039.                                             EndRefresh(win, TRUE);
  1040.  
  1041.                                             break;
  1042.  
  1043.                                         case IDCMP_VANILLAKEY:
  1044.  
  1045.                                             if (msg->Code == 27)
  1046.                                                 class  = CLOSEWINDOW;
  1047.  
  1048.                                             if (msg->Code == 13)
  1049.                                                 isDoubleClick = TRUE;
  1050.  
  1051.                                             break;
  1052.  
  1053.                                         case IDCMP_GADGETUP:
  1054.  
  1055.                                             active = msg->Code;
  1056.  
  1057.                                             if (active == lastSelection)
  1058.  
  1059.                                                 isDoubleClick = DoubleClick(lastSeconds, lastMicros, msg->Seconds, msg->Micros);
  1060.  
  1061.                                             lastSelection = msg->Code;
  1062.                                             lastSeconds   = msg->Seconds;
  1063.                                             lastMicros    = msg->Micros;
  1064.  
  1065.                                             break;
  1066.                                     }
  1067.  
  1068.                                     GT_ReplyIMsg(msg);
  1069.  
  1070.                                 } while ((class != CLOSEWINDOW) && (isDoubleClick == FALSE));
  1071.  
  1072.                                 if (class != CLOSEWINDOW) {
  1073.  
  1074.                                     if (node = SearchNode(list, active))
  1075.  
  1076.                                         strcpy(suggestion, node->ln_Name);
  1077.                                 }
  1078.  
  1079.                                 CloseWindow(win);
  1080.                             }
  1081.                         }
  1082.                     }
  1083.  
  1084.                     FreeVisualInfo(visualInfo);
  1085.                 }
  1086.             }
  1087.         }
  1088.     }
  1089. }
  1090.  
  1091. ///
  1092. /// "ispell"
  1093.  
  1094. /* --------------------------------- RunISpell ---------------------------------
  1095.  
  1096.  Run ISpell (if not yet running); increase user count
  1097.  
  1098. */
  1099.  
  1100. struct MsgPort *
  1101. RunISpell()
  1102. {
  1103.     struct MsgPort    *port;
  1104.     struct AutoConfig *autoConfig;
  1105.  
  1106.     // serialize access within this library
  1107.  
  1108.     ObtainSemaphore(&ISpellSemaphore);
  1109.  
  1110.     Forbid();
  1111.  
  1112.     // ISpell running/starting already ?
  1113.  
  1114.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1115.  
  1116.         ObtainSemaphore(&autoConfig->Semaphore);
  1117.  
  1118.         ++autoConfig->Users;
  1119.  
  1120.         ReleaseSemaphore(&autoConfig->Semaphore);
  1121.  
  1122.         port = WaitForISpell();
  1123.  
  1124.         if (port == NULL)
  1125.  
  1126.             Message("STARTUP FAILURE", TRUE, NULL);
  1127.     }
  1128.     else {
  1129.  
  1130.         port = FindPort("IRexxSpell");
  1131.  
  1132.         // run ISpell on our own ?
  1133.  
  1134.         if (port == NULL) {
  1135.  
  1136.             struct AutoConfig *autoConfig;
  1137.  
  1138.             if (autoConfig = (struct AutoConfig *)AllocVec(sizeof(struct AutoConfig), MEMF_PUBLIC | MEMF_CLEAR)) {
  1139.  
  1140.                 static UBYTE hashfile[255], language[255], personal[255], options[255], command[255], home[255];
  1141.  
  1142.                 __aligned struct FileInfoBlock fib = { 0 };
  1143.  
  1144.                 autoConfig->Semaphore.ss_Link.ln_Name = "WORDS";
  1145.                 autoConfig->Semaphore.ss_Link.ln_Pri  = 0;
  1146.                 autoConfig->Users                     = 1;
  1147.  
  1148.                 AddSemaphore   (&autoConfig->Semaphore);
  1149.                 ObtainSemaphore(&autoConfig->Semaphore);
  1150.  
  1151.                 // set default language
  1152.  
  1153.                 strcpy(language, "english");
  1154.  
  1155.                 // set default options
  1156.  
  1157.                 strcpy(options,  "-W 1");
  1158.  
  1159.                 // did user provide special startup arguments for ISpell ?
  1160.  
  1161.                 if (GetVar("WORDS.prefs", command, sizeof(command), GVF_GLOBAL_ONLY) != -1) {
  1162.  
  1163.                     if (*command) {
  1164.  
  1165.                         struct RDArgs *rdArgs, *args;
  1166.  
  1167.                         if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {
  1168.  
  1169.                             ULONG argArray[] = { 0, 0, 0, 0, 0 };
  1170.  
  1171.                             // make LF-terminated copy of command string (required by dos/readArgs):
  1172.  
  1173.                             strcat(command, "\12");
  1174.  
  1175.                             rdArgs->RDA_Source.CS_Buffer = command;
  1176.                             rdArgs->RDA_Source.CS_Length = strlen(command);
  1177.                             rdArgs->RDA_Source.CS_CurChr = 0;
  1178.                             rdArgs->RDA_DAList           = 0;
  1179.                             rdArgs->RDA_Buffer           = NULL;
  1180.  
  1181.                             if (args = ReadArgs("LANGUAGE/K,QUICKCHECK/K,BOUNDARYCHARS/K,BEEP/N,ARGS/F", argArray, rdArgs)) {
  1182.  
  1183.                                 if (argArray[0])             // LANGUAGE/K
  1184.                                     strcpy(language, (UBYTE *)argArray[0]);
  1185.  
  1186.                                 if (argArray[4])             // ARGS/F
  1187.                                     strcpy(options,  (UBYTE *)argArray[4]);
  1188.  
  1189.                                 FreeArgs(args);
  1190.                             }
  1191.                             else
  1192.                                 Message("Syntax error", TRUE, NULL);
  1193.  
  1194.                             FreeDosObject(DOS_RDARGS, rdArgs);
  1195.                         }
  1196.                     }
  1197.                 }
  1198.  
  1199.                 // personal dictionary file (for new words)
  1200.  
  1201.                 sprintf(personal, "/ispell/lib/.ispell_%s", language);
  1202.  
  1203.                 // standard dictionary file
  1204.  
  1205.                 sprintf(hashfile, "ispell:lib/%s.hash", language);
  1206.  
  1207.                 // dictionary available ?
  1208.  
  1209.                 if (FileInfo(hashfile, &fib)) {
  1210.  
  1211.                     ULONG avail = AvailMem(MEMF_LARGEST);
  1212.  
  1213.                     if (fib.fib_Size < avail) {
  1214.  
  1215.                         struct Window *win = Message("Starting ISpell...", FALSE, NULL);
  1216.  
  1217.                         // $HOME required by ISpell 3.1.18
  1218.  
  1219.                         if (GetVar("HOME", home, sizeof(home), GVF_GLOBAL_ONLY) == -1)
  1220.  
  1221.                             SetVar("HOME", "sys:", -1, GVF_GLOBAL_ONLY);
  1222.  
  1223.                         // run ISpell in host mode
  1224.  
  1225.                         if (*options)
  1226.                             sprintf(command, "ispell:bin/ispell -d%s -p%s -r %s", hashfile, personal, options);
  1227.                         else
  1228.                             sprintf(command, "ispell:bin/ispell -d%s -p%s -r", hashfile, personal);
  1229.  
  1230.                         // wait for completion of ISpell startup
  1231.  
  1232.                         if (DoExecute(command, TRUE, "NIL:", "ispell:", 32768, 1))
  1233.  
  1234.                             port = WaitForISpell();
  1235.  
  1236.                         if (port == NULL)
  1237.  
  1238.                             Message("STARTUP FAILURE", TRUE, win);
  1239.  
  1240.                         else if (win)
  1241.  
  1242.                             CloseWindow(win);
  1243.                     }
  1244.                     else {
  1245.  
  1246.                         if (fib.fib_Size <= AvailMem(MEMF_ANY))
  1247.                             Message("Out of RAM (fragmentation error)", TRUE, NULL);
  1248.                         else
  1249.                             Message("Out of RAM", TRUE, NULL);
  1250.                     }
  1251.                 }
  1252.                 else
  1253.                     Message("Dictionary not found", TRUE, NULL);
  1254.  
  1255.                 ReleaseSemaphore(&autoConfig->Semaphore);
  1256.  
  1257.                 if (port == NULL)
  1258.  
  1259.                     StopISpell();
  1260.             }
  1261.         }
  1262.     }
  1263.  
  1264.     Permit();
  1265.  
  1266.     ReleaseSemaphore(&ISpellSemaphore);
  1267.  
  1268.     return(port);
  1269. }
  1270.  
  1271.  
  1272. /* ------------------------------- WaitForISpell -------------------------------
  1273.  
  1274.  Wait for ISpell
  1275.  
  1276. */
  1277.  
  1278. struct MsgPort *
  1279. WaitForISpell()
  1280. {
  1281.     struct MsgPort *port;
  1282.     UWORD           try;
  1283.  
  1284.     for (try = 50; try-- && (port == NULL); Delay(10)) {
  1285.  
  1286.         Forbid();
  1287.  
  1288.         port = FindPort("IRexxSpell");
  1289.  
  1290.         Permit();
  1291.     }
  1292.  
  1293.     return(port);
  1294. }
  1295.  
  1296.  
  1297. /* -------------------------------- StopISpell ---------------------------------
  1298.  
  1299.  Decrement ISpell user count. Unload ISpell if unused.
  1300.  
  1301. */
  1302.  
  1303. void
  1304. StopISpell()
  1305. {
  1306.     struct AutoConfig *autoConfig;
  1307.  
  1308.     Forbid();
  1309.  
  1310.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1311.  
  1312.         ObtainSemaphore(&autoConfig->Semaphore);
  1313.  
  1314.         if (autoConfig->Users)
  1315.  
  1316.             --autoConfig->Users;
  1317.  
  1318.         ReleaseSemaphore(&autoConfig->Semaphore);
  1319.  
  1320.         if (autoConfig->Users == 0) {
  1321.  
  1322.             struct MsgPort *port;
  1323.  
  1324.             RemSemaphore    (&autoConfig->Semaphore);
  1325.             ObtainSemaphore (&autoConfig->Semaphore);
  1326.             ReleaseSemaphore(&autoConfig->Semaphore);
  1327.  
  1328.             if (port = CreateMsgPort()) {
  1329.  
  1330.                 if (autoConfig->Words) {
  1331.  
  1332.                     struct Screen *screen;
  1333.                     ULONG          lock;
  1334.  
  1335.                     // critical stuff (barely legal - don't do this at home, kids ;-)
  1336.  
  1337.                     lock = LockIBase(0);
  1338.  
  1339.                     // find frontmost screen
  1340.  
  1341.                     screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen;
  1342.  
  1343.                     // keep system frozen
  1344.  
  1345.                     Forbid();
  1346.  
  1347.                     // enable rendering functions
  1348.  
  1349.                     UnlockIBase(lock);
  1350.  
  1351.                     if (rtEZRequestTags("Save new words to user dictionary ?", "_SAVE|_cancel", NULL, NULL, RT_Underscore, '_', RTEZ_ReqTitle, "Words", RT_Screen, screen, TAG_DONE) == 1)
  1352.  
  1353.                         SendRexxCommand("IRexxSpell", "ADD A", port, NULL);
  1354.  
  1355.                     Permit();
  1356.                 }
  1357.  
  1358.                 SendRexxCommand("IRexxSpell", "EXIT", port, NULL);
  1359.  
  1360.                 DeleteMsgPort(port);
  1361.             }
  1362.  
  1363.             FreeVec(autoConfig);
  1364.         }
  1365.     }
  1366.  
  1367.     Permit();
  1368. }
  1369.  
  1370. ///
  1371. /// "arexx"
  1372.  
  1373. /* ---------------------------------- SendRexxCommand -------------------------
  1374.  
  1375.  Send ARexx message & wait for answer. Return pointer to result or NULL.
  1376.  
  1377. */
  1378.  
  1379. UBYTE *
  1380. SendRexxCommand(port, cmd, replyPort, buffer)
  1381.  
  1382. struct MsgPort *replyPort;
  1383. UBYTE          *cmd, *port, *buffer;
  1384. {
  1385.     struct MsgPort *rexxport;
  1386.  
  1387.     Forbid();
  1388.  
  1389.     if (rexxport = FindPort(port)) {
  1390.  
  1391.         struct RexxMsg *rexxMsg, *answer;
  1392.  
  1393.         if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
  1394.  
  1395.             if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
  1396.  
  1397.                 static ULONG result;
  1398.  
  1399.                 rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
  1400.  
  1401.                 PutMsg(rexxport, &rexxMsg->rm_Node);
  1402.  
  1403.                 do {
  1404.  
  1405.                     WaitPort(replyPort);
  1406.  
  1407.                     if (answer = (struct RexxMsg *)GetMsg(replyPort))
  1408.                         result = answer->rm_Result1;
  1409.  
  1410.                 } while (!answer);
  1411.  
  1412.                 Permit();
  1413.  
  1414.                 if (answer->rm_Result1 == RC_OK) {
  1415.  
  1416.                     if (answer->rm_Result2) {
  1417.  
  1418.                         if (buffer)
  1419.                             strcpy(buffer, (char *)answer->rm_Result2);
  1420.  
  1421.                         DeleteArgstring((char *)answer->rm_Result2);
  1422.                     }
  1423.                 }
  1424.  
  1425.                 DeleteArgstring((char *)ARG0(answer));
  1426.  
  1427.                 DeleteRexxMsg(answer);
  1428.  
  1429.                 return(&result);
  1430.             }
  1431.         }
  1432.     }
  1433.  
  1434.     Permit();
  1435.  
  1436.     return(NULL);
  1437. }
  1438.  
  1439. ///
  1440. /// "spellchecking"
  1441.  
  1442. /* ---------------------------- CheckWordCurrent -------------------------------
  1443.  
  1444.  Check word under or before cursor.
  1445.  
  1446. */
  1447.  
  1448. void
  1449. CheckWordCurrent(handle, apiMsg, suggestions)
  1450.  
  1451. struct APIClient  *handle;
  1452. struct APIMessage *apiMsg;
  1453. BOOL               suggestions;
  1454. {
  1455.     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  1456.  
  1457.     UBYTE *text, *last;
  1458.     UWORD  column;
  1459.  
  1460.     text = config->CurrentBuffer + config->Column;
  1461.     last = config->CurrentBuffer + config->CurrentLen;
  1462.  
  1463.     // find a character of the last word (ignore two white space characters if necessary)
  1464.  
  1465.     column = config->Column;
  1466.  
  1467.     if (column && (IsLetter[*text] == FALSE)) {
  1468.  
  1469.         --column;
  1470.         --text;
  1471.     }
  1472.  
  1473.     if (column && (IsLetter[*text] == FALSE)) {
  1474.  
  1475.         --column;
  1476.         --text;
  1477.     }
  1478.  
  1479.     // any character found ?
  1480.  
  1481.     if (IsLetter[*text]) {
  1482.  
  1483.         UWORD  wordLen;
  1484.         UBYTE *check;
  1485.  
  1486.         // find beginning of word
  1487.  
  1488.         while (column) {
  1489.  
  1490.             if (IsLetter[*(text - 1)]) {
  1491.  
  1492.                 --text;
  1493.                 --column;
  1494.             }
  1495.             else
  1496.                 break;
  1497.         }
  1498.  
  1499.         // find end of word
  1500.  
  1501.         for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) {
  1502.  
  1503.             // end of word detected ?
  1504.  
  1505.             if (IsLetter[*check] == FALSE) {
  1506.  
  1507.                 // verify end of word (boundary characters can be space OR letter)
  1508.  
  1509.                 if (IsBoundary[*check]) {
  1510.  
  1511.                     if ((check + 1) < last) {
  1512.  
  1513.                         if (IsLetter[*(check + 1)] == FALSE)
  1514.  
  1515.                             break;
  1516.                     }
  1517.                     else
  1518.                         break;
  1519.                 }
  1520.                 else
  1521.                     break;
  1522.             }
  1523.         }
  1524.  
  1525.         // check the word ?
  1526.  
  1527.         if ((wordLen > 1) && (wordLen <= 100)) {
  1528.  
  1529.             if (suggestions) {
  1530.  
  1531.                 UBYTE replacement[128];
  1532.  
  1533.                 if (CheckWord(handle, apiMsg, text, wordLen, replacement) == FALSE) {
  1534.  
  1535.                     if (*replacement)
  1536.  
  1537.                         Replace(handle, apiMsg, column, wordLen, replacement);
  1538.                 }
  1539.             }
  1540.             else if (CheckWord(handle, apiMsg, text, wordLen, NULL) == FALSE)
  1541.  
  1542.                 Beep(Volume);
  1543.         }
  1544.     }
  1545. }
  1546.  
  1547.  
  1548. /* --------------------------------- CheckWord ---------------------------------
  1549.  
  1550.  Check <word>. Handles approx. 100 characters. Return TRUE if ok. Writes
  1551.  replacement to <suggestion> (unless NULL).
  1552.  
  1553. */
  1554.  
  1555. BOOL
  1556. CheckWord(handle, apiMsg, word, len, suggestion)
  1557.  
  1558. struct APIClient  *handle;
  1559. struct APIMessage *apiMsg;
  1560. UBYTE             *word;
  1561. UWORD              len;
  1562. UBYTE             *suggestion;
  1563. {
  1564.     UBYTE command[128];
  1565.     BOOL  ok;
  1566.  
  1567.     ok = TRUE;
  1568.  
  1569.     if (suggestion)
  1570.  
  1571.         *suggestion = 0;
  1572.  
  1573.     // build command line for ISpell
  1574.  
  1575.     movmem(word, command, len);
  1576.  
  1577.     command[len] = 0;
  1578.  
  1579.     // suggestion requested ?
  1580.  
  1581.     if (suggestion) {
  1582.  
  1583.         UBYTE *result = ((struct BufferHandle *)handle)->Buffer;
  1584.  
  1585.         // send CHECK command to recieve detailed information (suggestions)
  1586.  
  1587.         strins(command, "CHECK ");
  1588.  
  1589.         if (SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer)) {
  1590.  
  1591.             if ((result[0] == 'o') && (result[1] == 'k')) {
  1592.  
  1593.                 ok = TRUE;
  1594.  
  1595.                 DisplayBeep(NULL);
  1596.             }
  1597.             else {
  1598.  
  1599.                 switch (result[0]) {
  1600.  
  1601.                     case '&':                        // spelling error, near misses available
  1602.  
  1603.                         UBYTE *end;
  1604.  
  1605.                         ok = FALSE;
  1606.  
  1607.                         if (end = strchr(result, ':')) {
  1608.  
  1609.                             // convert ISpell result string (suggestions) to exec list
  1610.  
  1611.                             struct List  list;
  1612.                             struct Node *node;
  1613.                             struct Node *next;
  1614.                             UWORD        count;
  1615.  
  1616.                             NewList(&list);
  1617.  
  1618.                             count = 0;
  1619.  
  1620.                             while (*end) {
  1621.  
  1622.                                 UBYTE *start;
  1623.  
  1624.                                 do {
  1625.  
  1626.                                     *end++ = 0;
  1627.  
  1628.                                 } while (*end && ((*end == ',') || (*end == ' ')));
  1629.  
  1630.                                 start = end;
  1631.  
  1632.                                 while (*end && (*end != 32) && (*end != ','))
  1633.  
  1634.                                     ++end;
  1635.  
  1636.                                 if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) {
  1637.  
  1638.                                     // node name: we point directly into the result string
  1639.  
  1640.                                     node->ln_Name = start;
  1641.  
  1642.                                     AddTail(&list, node);
  1643.  
  1644.                                     ++count;
  1645.                                 }
  1646.                             }
  1647.  
  1648.                             if (count) {
  1649.  
  1650.                                 // show suggestions
  1651.  
  1652.                                 ShowSpell(handle, apiMsg, &list, word, suggestion);
  1653.  
  1654.                                 // free list of suggestions
  1655.  
  1656.                                 for (node = list.lh_Head; next = node->ln_Succ; node = next)
  1657.  
  1658.                                     FreeVec(node);
  1659.                             }
  1660.                             else
  1661.                                 DisplayBeep(NULL);
  1662.                         }
  1663.                         else
  1664.                             DisplayBeep(NULL);
  1665.  
  1666.                         break;
  1667.  
  1668.                     case '?':                        // spelling error, no near misses
  1669.  
  1670.                         ok = FALSE;
  1671.  
  1672.                         DisplayBeep(NULL);
  1673.  
  1674.                         break;
  1675.  
  1676.                     case '*':                        // valid word
  1677.                     case '+':                        // valid after affix removal
  1678.                     case '-':                        // valid compound
  1679.  
  1680.                         ok = TRUE;
  1681.  
  1682.                         break;
  1683.  
  1684.                     default:
  1685.  
  1686.                         ok = FALSE;
  1687.  
  1688.                         DisplayBeep(0);
  1689.                 }
  1690.             }
  1691.         }
  1692.     }
  1693.     else {
  1694.  
  1695.         UBYTE *result = ((struct BufferHandle *)handle)->Buffer;
  1696.  
  1697.         strins(command, "QUICKCHECK ");
  1698.  
  1699.         if (SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer)) {
  1700.  
  1701.             if ((result[0] == 'o') && (result[1] == 'k'))
  1702.                 ok = TRUE;
  1703.             else
  1704.                 ok = FALSE;
  1705.         }
  1706.     }
  1707.  
  1708.     return(ok);
  1709. }
  1710.  
  1711.  
  1712. /* --------------------------------- LearnWord ---------------------------------
  1713.  
  1714.  Learn word <word>. Handles approx. 100 characters. Uncapitalize word if <smart>
  1715.  is TRUE.
  1716.  
  1717. */
  1718.  
  1719. void
  1720. LearnWord(handle, apiMsg, new, len, smart)
  1721.  
  1722. struct APIClient  *handle;
  1723. struct APIMessage *apiMsg;
  1724. UBYTE             *new;
  1725. UWORD              len;
  1726. BOOL               smart;
  1727. {
  1728.     struct AutoConfig *autoConfig;
  1729.  
  1730.     Forbid();
  1731.  
  1732.     if (autoConfig = (struct AutoConfig *)FindSemaphore("WORDS")) {
  1733.  
  1734.         UBYTE command[128];
  1735.  
  1736.         ObtainSemaphore(&autoConfig->Semaphore);
  1737.  
  1738.         ++autoConfig->Words;
  1739.  
  1740.         ReleaseSemaphore(&autoConfig->Semaphore);
  1741.  
  1742.         movmem(new, command, len);
  1743.  
  1744.         command[len] = 0;
  1745.  
  1746.         if (smart)
  1747.             Uncapitalize(command);
  1748.  
  1749.         strins(command, "QUICKADD ");
  1750.  
  1751.         SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer);
  1752.     }
  1753.     else
  1754.         Message("Communication error (semaphore not found)", TRUE, NULL);
  1755.  
  1756.     Permit();
  1757. }
  1758.  
  1759.  
  1760. /* ------------------------------ LearnWordCurrent -----------------------------
  1761.  
  1762.  Learn word under cursor. Uncapitalize word if <smart> is TRUE.
  1763.  
  1764. */
  1765.  
  1766. void
  1767. LearnWordCurrent(handle, apiMsg, smart)
  1768.  
  1769. struct APIClient  *handle;
  1770. struct APIMessage *apiMsg;
  1771. BOOL               smart;
  1772. {
  1773.     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  1774.  
  1775.     UBYTE *text, *last;
  1776.     UWORD  column;
  1777.  
  1778.     text = config->CurrentBuffer + config->Column;
  1779.     last = config->CurrentBuffer + config->CurrentLen;
  1780.  
  1781.     // find a character of the last word (ignore two white space characters if necessary)
  1782.  
  1783.     column = config->Column;
  1784.  
  1785.     if (column && (IsLetter[*text] == FALSE)) {
  1786.  
  1787.         --column;
  1788.         --text;
  1789.     }
  1790.  
  1791.     if (column && (IsLetter[*text] == FALSE)) {
  1792.  
  1793.         --column;
  1794.         --text;
  1795.     }
  1796.  
  1797.     // any character found ?
  1798.  
  1799.     if (IsLetter[*text]) {
  1800.  
  1801.         UWORD  wordLen;
  1802.         UBYTE *check;
  1803.  
  1804.         // find beginning of word
  1805.  
  1806.         while (column) {
  1807.  
  1808.             if (IsLetter[*(text - 1)]) {
  1809.  
  1810.                 --text;
  1811.                 --column;
  1812.             }
  1813.             else
  1814.                 break;
  1815.         }
  1816.  
  1817.         // find end of word
  1818.  
  1819.         for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) {
  1820.  
  1821.             // end of word detected ?
  1822.  
  1823.             if (IsLetter[*check] == FALSE) {
  1824.  
  1825.                 // verify end of word (boundary characters can be space OR letter)
  1826.  
  1827.                 if (IsBoundary[*check]) {
  1828.  
  1829.                     if ((check + 1) < last) {
  1830.  
  1831.                         if (IsLetter[*(check + 1)] == FALSE)
  1832.  
  1833.                             break;
  1834.                     }
  1835.                     else
  1836.                         break;
  1837.                 }
  1838.                 else
  1839.                     break;
  1840.             }
  1841.         }
  1842.  
  1843.         // learn the word ?
  1844.  
  1845.         if ((wordLen > 1) && (wordLen <= 100))
  1846.  
  1847.             LearnWord(handle, apiMsg, text, wordLen, smart);
  1848.     }
  1849. }
  1850.  
  1851.  
  1852. /* ------------------------------ IgnoreWordCurrent ----------------------------
  1853.  
  1854.  Ignore word under cursor. Uncapitalize word if <smart> is TRUE.
  1855.  
  1856. */
  1857.  
  1858. void
  1859. IgnoreWordCurrent(handle, apiMsg, smart)
  1860.  
  1861. struct APIClient  *handle;
  1862. struct APIMessage *apiMsg;
  1863. BOOL               smart;
  1864. {
  1865.     struct EditConfig *config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  1866.  
  1867.     UBYTE *text, *last;
  1868.     UWORD  column;
  1869.  
  1870.     text = config->CurrentBuffer + config->Column;
  1871.     last = config->CurrentBuffer + config->CurrentLen;
  1872.  
  1873.     // find a character of the last word (ignore two white space characters if necessary)
  1874.  
  1875.     column = config->Column;
  1876.  
  1877.     if (column && (IsLetter[*text] == FALSE)) {
  1878.  
  1879.         --column;
  1880.         --text;
  1881.     }
  1882.  
  1883.     if (column && (IsLetter[*text] == FALSE)) {
  1884.  
  1885.         --column;
  1886.         --text;
  1887.     }
  1888.  
  1889.     // any character found ?
  1890.  
  1891.     if (IsLetter[*text]) {
  1892.  
  1893.         UWORD  wordLen;
  1894.         UBYTE *check;
  1895.  
  1896.         // find beginning of word
  1897.  
  1898.         while (column) {
  1899.  
  1900.             if (IsLetter[*(text - 1)]) {
  1901.  
  1902.                 --text;
  1903.                 --column;
  1904.             }
  1905.             else
  1906.                 break;
  1907.         }
  1908.  
  1909.         // find end of word
  1910.  
  1911.         for (wordLen = 1, check = text + 1; check < last; ++wordLen, ++check) {
  1912.  
  1913.             // end of word detected ?
  1914.  
  1915.             if (IsLetter[*check] == FALSE) {
  1916.  
  1917.                 // verify end of word (boundary characters can be space OR letter)
  1918.  
  1919.                 if (IsBoundary[*check]) {
  1920.  
  1921.                     if ((check + 1) < last) {
  1922.  
  1923.                         if (IsLetter[*(check + 1)] == FALSE)
  1924.  
  1925.                             break;
  1926.                     }
  1927.                     else
  1928.                         break;
  1929.                 }
  1930.                 else
  1931.                     break;
  1932.             }
  1933.         }
  1934.  
  1935.         // ignore the word ?
  1936.  
  1937.         if ((wordLen > 1) && (wordLen <= 100)) {
  1938.  
  1939.             UBYTE command[128];
  1940.  
  1941.             movmem(text, command, wordLen);
  1942.  
  1943.             command[wordLen] = 0;
  1944.  
  1945.             if (smart)
  1946.                 Uncapitalize(command);
  1947.  
  1948.             strins(command, "ACCEPT ");
  1949.  
  1950.             SendRexxCommand("IRexxSpell", command, ((struct BufferHandle *)handle)->Port, ((struct BufferHandle *)handle)->Buffer);
  1951.         }
  1952.     }
  1953. }
  1954.  
  1955. ///
  1956. /// "dice"
  1957.  
  1958. #ifdef _DCC
  1959.  
  1960. /* ------------------------------- CreateGadget --------------------------------
  1961.  
  1962.  Stub function (missing in linker lib of DICE 2.0.54r)
  1963.  
  1964. */
  1965.  
  1966. struct Gadget *
  1967. CreateGadget(ULONG kind, struct Gadget *previous, struct NewGadget *newgad, ULONG firstTag, ...)
  1968. {
  1969.     return(CreateGadgetA(kind, previous, newgad, (struct TagItem *)&firstTag));
  1970. }
  1971.  
  1972. /* ----------------------------- GT_SetGadgetAttrs -----------------------------
  1973.  
  1974.  Stub function (missing in linker lib of DICE 2.0.54r)
  1975.  
  1976. */
  1977.  
  1978. void
  1979. GT_SetGadgetAttrs(struct Gadget *gad, struct Window *win, struct Requester *req, ULONG firstTag, ...)
  1980. {
  1981.     GT_SetGadgetAttrsA(gad, win, req, (struct TagItem *)&firstTag);
  1982. }
  1983.  
  1984. /* ------------------------------- DrawBevelBox --------------------------------
  1985.  
  1986.  Stub function (missing in linker lib of DICE 2.0.54r)
  1987.  
  1988. */
  1989.  
  1990. void
  1991. DrawBevelBox(rport, left, top, width, height, firsttag, ...)
  1992.  
  1993. struct RastPort *rport;
  1994. LONG             left, top, width, height;
  1995. Tag              firsttag;
  1996. {
  1997.     DrawBevelBoxA(rport, left, top, width, height, (struct TagItem *)&firsttag);
  1998. }
  1999.  
  2000. #endif
  2001.  
  2002. ///
  2003.